package com.dtflys.easyel.runtime.eval;

import com.dtflys.easyel.ast.ASTIndexExpression;
import com.dtflys.easyel.exception.EasyElAccessException;
import com.dtflys.easyel.exception.EasyElEvalException;
import com.dtflys.easyel.exception.EasyElNullException;
import com.dtflys.easyel.runtime.EasyElRuntimeContext;
import com.dtflys.easyel.runtime.wrapper.NumberWrapper;
import com.dtflys.easyel.utils.AccessUtils;
import com.dtflys.easyel.utils.MetaHelper;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;


/**
 * @author gongjun[jun.gong@thebeastshop.com]
 * @since v1.0.0
 */
public class IndexEvaluator {

    public Object evaluate(EasyElRuntimeContext context, ASTIndexExpression indexExpression, Object left, Object index) {
        if (left == null) {
            throw new EasyElNullException(context, indexExpression.getLeft());
        }
        if (index == null) {
            throw new EasyElNullException(context, indexExpression.getIndex());
        }
        left = MetaHelper.unwrap(left);
        Class clazz = left.getClass();

        if (Map.class.isAssignableFrom(clazz)) {
            index = MetaHelper.unwrap(index);
            return MetaHelper.wrap(((Map) left).get(index));
        }

        Integer intIndex = null;
        if (index instanceof NumberWrapper) {
            intIndex = ((NumberWrapper) index).intValue();
        }
        if (clazz.isArray() || List.class.isAssignableFrom(clazz)) {
            if (clazz.isArray()) {
                if (intIndex < 0) {
                    intIndex = Array.getLength(left) + intIndex;
                }
                return MetaHelper.wrap(Array.get(left, intIndex));
            }
            if (List.class.isAssignableFrom(clazz)) {
                if (intIndex < 0) {
                    intIndex = ((List) left).size() + intIndex;
                }
                return MetaHelper.wrap(((List) left).get(intIndex));
            }
        }
        try {
            return AccessUtils.accessObject(left, index.toString());
        } catch (IllegalAccessException e) {
            throw new EasyElEvalException(context, indexExpression.getIndex(), e.getMessage(), e);
        } catch (InvocationTargetException e) {
            throw new EasyElEvalException(context, indexExpression.getIndex(), e.getMessage(), e);
        } catch (EasyElAccessException e) {
            throw new EasyElEvalException(context, indexExpression.getIndex(), e.getMessage());
        }
    }

}
